﻿namespace Blaze.Framework.Loaders
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using Logging;
    using UnityEngine;

    /// <summary>
    /// 用于加载网络图片。
    /// Uri = http://www.onyuan.com/images/game/1.png
    /// </summary>
    public class WebImageLoader : AssetLoaderBase
    {
        /// <summary>
        /// 获取加载成功后的精灵图片。
        /// </summary>
        public Sprite Sprite
        {
            get
            {
                if (mSprite != null)
                    return mSprite;

                if (Texture2D == null)
                    return null;

                mSprite = Sprite.Create(Texture2D, new Rect(0, 0, Texture2D.width, Texture2D.height), new Vector2(0.5f, 0.5f));
                return mSprite;
            }
        }

        /// <summary>
        /// 获取加载成功后的图片。
        /// </summary>
        public Texture2D Texture2D
        {
            get { return Result as Texture2D; }
        }

        /// <summary>
        /// 设置一个值，表示是否缓存下载后的图片。
        /// </summary>
        public WebImageLoader SetCache(bool shouldCache)
        {
            mShouldCache = shouldCache;
            return this;
        }

        /// <summary>
        /// 设置最大自动重试次数，0表示不重试，-1表示无限重试。
        /// </summary>
        public WebImageLoader SetMaxRetry(int max)
        {
            mMaxRetry = max;
            return this;
        }

        /// <summary>
        /// 当加载器需要被释放时调用此方法。
        /// </summary>
        protected override void OnDispose()
        {
            if (mWebTextureLoader != null)
            {
                AssetLoaderPool.Put(mWebTextureLoader);
                mWebTextureLoader = null;
            }

            if (mBinaryLoader != null)
            {
                AssetLoaderPool.Put(mBinaryLoader);
                mBinaryLoader = null;
            }

            mSprite = null;
            mCachePath = null;
            base.OnDispose();
        }

        /// <summary>
        /// 当加载器启动时调用此方法。
        /// </summary>
        protected override void OnStart()
        {
            var hash = calculateHashString(Uri);
            var relativePath = mCacheImageDirectoryName + "/" + hash;
            mCachePath = PathUtility.GetTemporaryCachePath(relativePath);
            if (File.Exists(mCachePath))
                loadFromLocal(relativePath);
            else
                loadFromRemote();
        }

        private static string calculateHashString(string url)
        {
            var bytes = Encoding.ASCII.GetBytes(url);
            var hash = mMd5.ComputeHash(bytes);
            var buffer = new StringBuilder(hash.Length * 2);
            for (var i = 0; i < hash.Length; i++)
                buffer.AppendFormat("{0:x2}", hash[i]);
            return buffer.ToString();
        }

        private void cacheToFileIfNecessary(byte[] bytes)
        {
            if (!mShouldCache)
                return;
            var dir = Path.GetDirectoryName(mCachePath);
            try
            {
                FileUtility.EnsureDirectory(dir);
                File.WriteAllBytes(mCachePath, bytes);
            }
            catch (Exception e)
            {
                mLog.Error($"[WebImageLoader]缓存文件失败 url={Uri} file={mCachePath} error={e.Message}");
            }
        }

        private void loadFromLocal(string relativePath)
        {
            mBinaryLoader = AssetLoaderPool.Get<BinaryLoader>(relativePath)
                .AsSync()
                .OnFinish(loader =>
                {
                    if (IsDisposed)
                        return;

                    if (!loader.IsOk)
                    {
                        Finish(loader.Error, null);
                        return;
                    }

                    var texture2d = new Texture2D(2, 2, TextureFormat.RGBA32, false);
                    texture2d.LoadImage(loader.Bytes, true);
                    Finish(loader.Error, texture2d);
                })
                .FluentStart();
        }

        private void loadFromRemote()
        {
            mWebTextureLoader = AssetLoaderPool.Get<UnityWebRequestTextureLoader>(Uri)
                .SetMaxRetry(mMaxRetry)
                .AsAsync()
                .OnFinish(loader =>
                {
                    if (IsDisposed)
                        return;

                    if (!loader.IsOk)
                    {
                        Finish(loader.Error, null, loader.IsSightlyError);
                        return;
                    }

                    cacheToFileIfNecessary(loader.Bytes);
                    Finish(loader.Error, loader.Texture2D);
                })
                .FluentStart();
        }

        private const string mCacheImageDirectoryName = "webimage";
        private static readonly Log mLog = LogManager.GetLogger(typeof(WebImageLoader));
        private static readonly MD5 mMd5 = MD5.Create();
        private BinaryLoader mBinaryLoader;
        private string mCachePath;
        private bool mShouldCache = true;
        private int mMaxRetry;
        private Sprite mSprite;
        private UnityWebRequestTextureLoader mWebTextureLoader;
    }
}